package com.pep.mgmt.controller;


import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.pep.mgmt.controller.vo.ConfigVo;
import com.pep.mgmt.controller.vo.ConfigVrfVo;
import com.pep.mgmt.controller.vo.RcstIpTablesRuleVo;
import com.pep.mgmt.controller.vo.RunningProgramVo;
import com.pep.mgmt.controller.vo.RunningStatusVo;
import com.pep.mgmt.model.entity.ConfigEntity;
import com.pep.mgmt.model.entity.RcstIpTablesRule;
import com.pep.mgmt.model.entity.RunningEntity;
import com.pep.mgmt.model.entity.VrfEntity;
import com.pep.mgmt.service.PepMgmtService;
import com.pep.mgmt.service.ShellCmdService;
import com.pep.mgmt.util.CONST;
import com.pep.mgmt.util.Result;

import lombok.extern.slf4j.Slf4j;

@RestController
@Slf4j
@RequestMapping(value = "/pep_config")
public class ConfigurationController {
	
  private final ShellCmdService shellCmdService;
  private final PepMgmtService pepMgmtService;

  private boolean isStarted;

  public void startConfigurationController() {
    log.info("startConfigurationController");
    isStarted = true;
  }

  public void stopConfigurationController() {
    log.info("stopConfigurationController");
    isStarted = false;
  }

  public ConfigurationController(ShellCmdService shellCmdService, PepMgmtService pepMgmtService) {
    this.shellCmdService = shellCmdService;
    this.pepMgmtService = pepMgmtService;
    isStarted = false;
  }

  private boolean isAllowedToService() {
    return isStarted;
  }

  @GetMapping(path="/vrf")
  public Result<Object> getAllVrfs() {
    log.info("get all vrfs");

    if (!isAllowedToService())
      return Result.error(4000, CONST.MSG_4000, null);

    List<VrfEntity> vrfEntities = pepMgmtService.findAllVrfs();
    List<ConfigVrfVo> configVrfVoes = new ArrayList<>();

    for (VrfEntity ve : vrfEntities) {
      ConfigVrfVo cvv = new ConfigVrfVo();
      cvv.setDefautlGW(ve.getDefaultGW());
      cvv.setVlanId(ve.getVlanId());
      cvv.setVlanIpAddr(ve.getVlanIpAddr());
      cvv.setVrfName(ve.getVrfName());
      configVrfVoes.add(cvv);
    }

    return Result.success("get all vrfs information successfully!", configVrfVoes);
  }

  @GetMapping(path="/vrf/{vrfName}")
  public Result<Object> getVrf(@PathVariable String vrfName) {
    log.info("get vrf {}", vrfName);

    if (!isAllowedToService())
      return Result.error(4000, CONST.MSG_4000, null);

    List<VrfEntity> vrfEntities = pepMgmtService.findVrfByName(vrfName);
    ConfigVrfVo cvv = new ConfigVrfVo();
    
    if (vrfEntities.isEmpty()) {
      return Result.error(4008, CONST.MSG_4008, null);
    }

    for (VrfEntity ve : vrfEntities) {
      cvv.setDefautlGW(ve.getDefaultGW());
      cvv.setVlanId(ve.getVlanId());
      cvv.setVlanIpAddr(ve.getVlanIpAddr());
      cvv.setVrfName(ve.getVrfName());
    }

    return Result.success("get vrf information successfully!", cvv);
  }
  
  @PostMapping(path="/vrf")
  public Result<Object> createVlan(@RequestBody ConfigVrfVo configVrfVo) {
    log.info("create vrf " + configVrfVo);
    if (!isAllowedToService())
        return Result.error(4000, CONST.MSG_4000, null); 

    if (pepMgmtService.vrfCountByVrfNameAndVlanId(configVrfVo.getVrfName(), configVrfVo.getVlanId()) > 0) {
      log.warn(CONST.MSG_4001);
      return Result.error(4001, CONST.MSG_4001, null);
    }
      
    VrfEntity vrfEntity = new VrfEntity();
    vrfEntity.setVlanId(configVrfVo.getVlanId());
    vrfEntity.setDefaultGW(configVrfVo.getDefautlGW());
    vrfEntity.setVlanIpAddr(configVrfVo.getVlanIpAddr());
    vrfEntity.setVrfName(configVrfVo.getVrfName());
    String result = shellCmdService.someMethod("./bash/add_vrf.sh " + configVrfVo.getVlanId() + " " + configVrfVo.getVlanIpAddr() + " " + configVrfVo.getDefautlGW());
    log.info("result add_vrf.sh {}", result);
    if (result.contains(CONST.SUCCESS_MARK)) {
      log.info("create vrf in database");
      pepMgmtService.createVlan(vrfEntity);
      return Result.success("create vrf successfully!", null);
    } else {
      log.error("create vrf failed");
      return Result.error(4002, CONST.MSG_4002, null);
    }
  
  }

  @DeleteMapping(path="/vrf/{vrfName}")
  public Result<Object> deleteVrf(@PathVariable String vrfName) {

    log.info("delete vrf {}", vrfName);

    if (!isAllowedToService())
      return Result.error(4000, CONST.MSG_4000, null);  
      
    List<RunningEntity> runningEntities = pepMgmtService.findRunningProgramByVrfName(vrfName);
    log.info("find runningEntity with size {}", runningEntities.size());
    boolean isVrfAllowedToDelete = true;

    for (RunningEntity re : runningEntities) {
      if (!re.isRequiredStopped()) {
        isVrfAllowedToDelete = false;
      }
    }

    if (!isVrfAllowedToDelete) {
      log.error("delete vrf failed due to running program!");
      return Result.error(4007, CONST.MSG_4007, null);
    }

    String result = shellCmdService.someMethod("./bash/del_vrf.sh " + vrfName);
    log.info("result del_vrf.sh {}", result);
    if (result.contains(CONST.SUCCESS_MARK)) {
      log.info("delete vrf {} ", vrfName);
      pepMgmtService.deleteByVrfName(vrfName);
      return Result.success("delete vrf successfully!", null);
    } else {
      log.error("delete vrf failed");
      return Result.error(4003, CONST.MSG_4003, null);
    }
  }

  @GetMapping(path="/running")
  public Result<Object> getAllRunningPrograms() {
    log.info("GET /running");
  
    if (!isAllowedToService())
      return Result.error(4000, CONST.MSG_4000, null); 

    List<RunningEntity> runningEntityList = pepMgmtService.findAllRunningPrograms();
    List<RunningProgramVo> runningProgramList = new ArrayList<>();

    for(RunningEntity re : runningEntityList) {
      if (!re.isRequiredStopped()) {
        RunningProgramVo rpo = new RunningProgramVo();
        rpo.setLocalBindIpAddr(re.getLocalBindIpAddr());
        rpo.setVrfName(re.getVrfName());
        rpo.setStartCmd(re.getLaunchCmd());
        rpo.setWorkMode(re.getWorkMode());
        runningProgramList.add(rpo);
      }
    }
    return Result.success("get all running programs successfully!", runningProgramList);
  }

  // @GetMapping(path="/running")
  // public Result<Object> getAllRunningPrograms() {
  //   log.info("GET /running");
  
  //   if (!isAllowedToService())
  //     return Result.error(4000, CONST.MSG_4000, null); 


  //   RunningStatusVo runningStatusVo = new RunningStatusVo();

  //   List<RunningEntity> runningEntityList = pepMgmtService.findAllRunningPrograms();
  //   List<RunningProgramVo> runningProgramList = new ArrayList<>();

  //   for(RunningEntity re : runningEntityList) {
  //     if (!re.isRequiredStopped()) {
  //       RunningProgramVo rpo = new RunningProgramVo();
  //       rpo.setLocalBindIpAddr(re.getLocalBindIpAddr());
  //       rpo.setVrfName(re.getVrfName());
  //       rpo.setStartCmd(re.getLaunchCmd());
  //       rpo.setWorkMode(re.getWorkMode());
  //       runningProgramList.add(rpo);
  //     }
  //   }

  //   runningStatusVo.setRunningPrograms(runningProgramList);
    
  //   String result = shellCmdService.someMethod("./bash/tcp_info.sh ue_count");
  //   log.info("result./bash/tcp_info.sh ue_count {}", result);
  //   runningStatusVo.setUeCounts(result.trim());

  //   result = shellCmdService.someMethod("./bash/tcp_info.sh connect_count");
  //   log.info("result./bash/tcp_info.sh connect_count {}", result);
  //   runningStatusVo.setTotalTcpConnections(result.trim());

  //   result = shellCmdService.someMethod("./bash/tcp_info.sh connect_count_svn");
  //   log.info("result./bash/tcp_info.sh connect_count_svn {}", result);
  //   runningStatusVo.setSvnAndConnections(result.trim());

  //   return Result.success("get all running status successfully!", runningStatusVo);
  // }

  @PostMapping(path="/running")
  public Result<Object> startRunningProgram(@RequestBody RunningProgramVo rpo) {
    
    log.info("put /running {}", rpo.toString());

    if (!isAllowedToService())
      return Result.error(4000, CONST.MSG_4000, null); 

    List<RunningEntity> runningEntities = pepMgmtService.findRunningProgramByVrfName(rpo.getVrfName());
    for (RunningEntity re : runningEntities) {
      if (re.isRequiredStopped()) {
        log.info(CONST.MSG_4010);
        return Result.error(4010, CONST.MSG_4010, null);
      } else {
        log.warn(CONST.MSG_4001);
        return Result.error(4001, CONST.MSG_4001, null);
      }
    }
  
    String result = null;
    if (CONST.TEST_MODE)
      result = shellCmdService.someMethod("./start_program.sh " + rpo.getVrfName() + " " + rpo.getStartCmd());
    else 
      result = shellCmdService.someMethod("./bash/start_tcp.sh " + rpo.getVrfName() + " " + rpo.getLocalBindIpAddr() + " " + rpo.getWorkMode());
    log.info("result to start {}", result);
    long pId = -1;
    try {
      pId = Long.parseLong(result.trim());
      log.info("pId {}", pId);
    } catch (NumberFormatException nfe) {
      log.warn("NumberFormatException {}", nfe.getMessage());
    }
    if (pId > 0) {
      RunningEntity runningEntity = new RunningEntity();
      runningEntity.setVrfName(rpo.getVrfName());
      runningEntity.setPId(pId);
      runningEntity.setLocalBindIpAddr(rpo.getLocalBindIpAddr());
      runningEntity.setLaunchCmd(rpo.getStartCmd());
      runningEntity.setWorkMode(rpo.getWorkMode());
      pepMgmtService.addOneRunningProgram(runningEntity);
      return Result.success("start one running program successfully!", null);
    } else {
      return Result.error(4004, CONST.MSG_4004, null);
    }
  }
   
  @GetMapping(path="/running/{vrfName}")
  public Result<Object> getRunningProgram(@PathVariable String vrfName) {
    
    log.info("get /running {}", vrfName);

    if (!isAllowedToService())
      return Result.error(4000, CONST.MSG_4000, null); 

    List<RunningEntity> runningEntities = pepMgmtService.findRunningProgramByVrfName(vrfName);
    RunningProgramVo rpo = new RunningProgramVo();

    if (runningEntities.isEmpty()) {
      log.warn(CONST.MSG_4009);
      return Result.error(4009, CONST.MSG_4009, null);
    } 

    for (RunningEntity re: runningEntities) {
      rpo.setLocalBindIpAddr(re.getLocalBindIpAddr());
      rpo.setVrfName(re.getVrfName());
      rpo.setWorkMode(re.getWorkMode());
      rpo.setStartCmd(re.getLaunchCmd());
    }
    return Result.success("get the running program successfully!", rpo);
  }

  @DeleteMapping(path="/running/{vrfName}")
  public Result<Object> stopRunningProgram(@PathVariable String vrfName) {
    
    log.info("delete /running {}", vrfName);
    if (!isAllowedToService())
      return Result.error(4000, CONST.MSG_4000, null); 

    List<RunningEntity> runningEntities = pepMgmtService.findRunningProgramByVrfName(vrfName);
    log.trace("find runningEntity with size {}", runningEntities.size());

    for (RunningEntity re :runningEntities) {
      log.info("RunningEntity {}", re);
      String cmd = "./bash/white_list.sh disable 0.0.0.0 " + vrfName;
      log.info("execute cmd {}", cmd);
      String result = shellCmdService.someMethod(cmd);
      log.info("result {} {}", cmd, result);
      if (!result.contains(CONST.SUCCESS_MARK)) {
        log.error(CONST.MSG_4011);
        return Result.error(4011, CONST.MSG_4011, null);
      }
    }

    pepMgmtService.markRunningProgramStopped(vrfName);
    return Result.success("stop the running program successfully!", null);
  }
  
  @PostMapping(path="/iptables_rule")
  public Result<Object> createRecstIpTablesRules(@RequestBody RcstIpTablesRuleVo rcstIpTablesRuleVo) {
    
    log.info("create rcst iptables rules {}", rcstIpTablesRuleVo);

    if (!isAllowedToService())
      return Result.error(4000, CONST.MSG_4000, null); 

    if (pepMgmtService.rcstIpTablesRuleCountByVrfNameAndRcstIpAddr(rcstIpTablesRuleVo.getVrfName(), rcstIpTablesRuleVo.getRcstIpAddr()) > 0) {
      log.warn(CONST.MSG_4001);
      return Result.error(4001, CONST.MSG_4001, null);
    }

    RcstIpTablesRule rcstIpTablesRule = new RcstIpTablesRule();
    
    rcstIpTablesRule.setVrfName(rcstIpTablesRuleVo.getVrfName());
    rcstIpTablesRule.setRcstIpAddr(rcstIpTablesRuleVo.getRcstIpAddr());
    rcstIpTablesRule.setIptablesRules(rcstIpTablesRuleVo.getIpTablesRules());

    //run script to create iptables rules
    String runCmd = "./bash/white_list.sh add " + rcstIpTablesRule.getVrfName() + " " + rcstIpTablesRule.getIptablesRules();
    log.info("runCmd {}", runCmd);
    String result = shellCmdService.someMethod(runCmd);
    log.info("result white_list.sh {}", result);
    if (result.contains(CONST.SUCCESS_MARK)) {
      String fileName = rcstIpTablesRuleVo.getRcstIpAddr() + "_" + rcstIpTablesRuleVo.getVrfName();
      pepMgmtService.saveConfigToFile(fileName, CONST.CONFIG_FILE_PATH, rcstIpTablesRuleVo.getIpTablesRules());
      pepMgmtService.addRcstIpTableRule(rcstIpTablesRule);
      return Result.success("create rcst iptables rules successfully!", null);
    } else {
      return Result.error(4002, "create rcst iptables rules failed!", null);
    }
  }

  @GetMapping(path="/iptables_rule")
  public Result<Object> getAllRecstIpTablesRules() {
  
    log.info("get all rcst iptables rule");

    if (!isAllowedToService())
      return Result.error(4000, CONST.MSG_4000, null); 

    List<RcstIpTablesRule> rcstIpTablesRules = pepMgmtService.getAllRcstIpTableRules();
    List<RcstIpTablesRuleVo> rcstIpTablesRuleVos = new ArrayList<>();

    for (RcstIpTablesRule ritr :rcstIpTablesRules) {
      RcstIpTablesRuleVo ritrVo = new RcstIpTablesRuleVo();
      ritrVo.setIpTablesRules(ritr.getIptablesRules());
      ritrVo.setRcstIpAddr(ritr.getRcstIpAddr());
      ritrVo.setVrfName(ritr.getVrfName());
      rcstIpTablesRuleVos.add(ritrVo);
    }

   return Result.success("get all rcst iptables rules successfully!", rcstIpTablesRuleVos);
  }

  @GetMapping(path="/iptables_rule/")
  public Result<Object> getRecstIpTablesRules(@RequestBody RcstIpTablesRuleVo rcstIpTablesRuleVo) {
    
    log.info("get rcst iptables rules {}", rcstIpTablesRuleVo);

    if (!isAllowedToService())
       return Result.error(4000, CONST.MSG_4000, null); 

    Optional<RcstIpTablesRule> rcstIpTablesRuleOptional = pepMgmtService.findRcstIpTableRulesByVrfNameAndRcstIpAddr(rcstIpTablesRuleVo.getVrfName(), rcstIpTablesRuleVo.getRcstIpAddr());

    if (rcstIpTablesRuleOptional.isPresent()) {
      RcstIpTablesRule rcstIpTablesRule = rcstIpTablesRuleOptional.get();
      RcstIpTablesRuleVo ritrVo = new RcstIpTablesRuleVo();
      ritrVo.setIpTablesRules(rcstIpTablesRule.getIptablesRules());
      ritrVo.setVrfName(rcstIpTablesRule.getVrfName());
      ritrVo.setRcstIpAddr(rcstIpTablesRule.getRcstIpAddr());
      return Result.success("get rcst iptables rules successfully!", ritrVo);
    } else {  
      return Result.error(4006, CONST.MSG_4006, null);
    }
  }

  @DeleteMapping(path="/iptables_rule")
  public Result<Object> deleteRecstIpTablesRules(@RequestBody RcstIpTablesRuleVo rcstIpTablesRuleVo) {
    
    log.info("delete rcst iptables rules {}", rcstIpTablesRuleVo);

    if (!isAllowedToService())
       return Result.error(4000, CONST.MSG_4000, null); 

    Optional<RcstIpTablesRule> rcstIpTablesRuleOptional = pepMgmtService.findRcstIpTableRulesByVrfNameAndRcstIpAddr(rcstIpTablesRuleVo.getVrfName(), rcstIpTablesRuleVo.getRcstIpAddr());

    if (rcstIpTablesRuleOptional.isPresent()) {
      RcstIpTablesRule rcstIpTablesRule = rcstIpTablesRuleOptional.get();
      // run script to delete iptables rules
      String runCmd = "./bash/white_list.sh del rcst " + rcstIpTablesRule.getRcstIpAddr() + " " + rcstIpTablesRule.getVrfName();
      log.info("runCmd {}", runCmd);
      String result = shellCmdService.someMethod(runCmd);
      log.info("result white_list.sh {}", result);
      if (result.contains(CONST.SUCCESS_MARK)) {
        String fileName = rcstIpTablesRuleVo.getRcstIpAddr() + "_" + rcstIpTablesRuleVo.getVrfName();
        String content = "";
        pepMgmtService.saveConfigToFile(fileName, CONST.CONFIG_FILE_PATH, content);
        pepMgmtService.deleteRcstIpTableRuleByVrfNameAndRcstIpAddr(rcstIpTablesRuleVo.getVrfName(), rcstIpTablesRuleVo.getRcstIpAddr());
        return Result.success("delete rcst iptables rules successfully!", null);
      } else {
        return Result.error(4005, CONST.MSG_4005, null);
      }
    } else {  
      return Result.error(4006, CONST.MSG_4006, null);
    }
  }

  @PutMapping(path="/config")
  public Result<Object> updateTcpClientConfig(@RequestBody ConfigVo configVo) {
    log.info("update config {}", configVo);

    if (!isAllowedToService())
      return Result.error(4000, CONST.MSG_4000, null); 

    ConfigEntity ce = new ConfigEntity();
    ce.setClientTcpAlgo(configVo.getClientTcpAlgo());
    ce.setTunnelTcpAlgo(configVo.getTunnelTcpAlgo());
    ce.setTunnelTcpHighWaterMark(configVo.getTunnelTcpHighWaterMark());
    ce.setTunnelTcpLowWaterMark(configVo.getTunnelTcpLowWaterMark());
    ce.setClientTcpHighWaterMark(configVo.getClientTcpHighWaterMark());
    ce.setClientTcpRecvBuffer(configVo.getClientTcpRecvBuffer());  
    pepMgmtService.updateConfig(ce, false);  
    return Result.success("update config success!", null);
  }


  @GetMapping(path="/config")
  public Result<Object> getTcpClientConfig() {
    log.info("get config");

    if (!isAllowedToService())
      return Result.error(4000, CONST.MSG_4000, null); 

    List<ConfigEntity> configEntities = pepMgmtService.getConfig();
    ConfigVo cv = new ConfigVo();
    for (ConfigEntity ce : configEntities) {
      cv.setClientTcpAlgo(ce.getClientTcpAlgo());
      cv.setClientTcpHighWaterMark(ce.getClientTcpHighWaterMark());
      cv.setClientTcpRecvBuffer(ce.getClientTcpRecvBuffer());
      cv.setTunnelTcpAlgo(ce.getTunnelTcpAlgo());
      cv.setTunnelTcpHighWaterMark(ce.getTunnelTcpHighWaterMark());
      cv.setTunnelTcpLowWaterMark(ce.getTunnelTcpLowWaterMark());
    }
    return Result.success("get config success!", cv);
  }

  @GetMapping(path="/test")
  public Result<Object> getTestController() {
    
    log.info("get test");
    if (!isAllowedToService())
      return Result.error(4000, CONST.MSG_4000, null); 

    RcstIpTablesRule rcstIpTablesRule = new RcstIpTablesRule();
    rcstIpTablesRule.setVrfName("testvrf1");
    rcstIpTablesRule.setRcstIpAddr("testipaddr");
    rcstIpTablesRule.setIptablesRules("test iptables rule");
    pepMgmtService.saveConfigToFile("test_conf.conf", CONST.CONFIG_FILE_PATH, rcstIpTablesRule.toString());
    pepMgmtService.addRcstIpTableRule(rcstIpTablesRule);

    return Result.success("get test success!", null);
  }

  
  @DeleteMapping(path="/test")
  public Result<Object> deleteTestController() {
    
    log.info("delete test");
    if (!isAllowedToService())
      return Result.error(4000, CONST.MSG_4000, null); 

    pepMgmtService.deleteRcstIpTableRuleByVrfNameAndRcstIpAddr("testvrf1", "testipaddr");

    return Result.success("detele test success!", null);
  }

}
